/*
 * Author: Kishore Reddy
 * Url: http://NHelpernet.codeplex.com/
 * Title: NHelper.NET
 * Copyright: � 2009 Kishore Reddy
 * License: LGPL License
 * LicenseUrl: http://NHelpernet.codeplex.com/license
 * Description: A C# based .NET 3.5 Open-Source collection of reusable components.
 * Usage: Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Text.RegularExpressions;
using System.Collections.Specialized;
using System.IO;


namespace Youz.Common.Helper
{
    /// <summary>
    /// Url helper to provide various utitlity methods.
    /// 
    /// 1. Provide valid url's for either local development maching running
    ///    a website from visual studio OR IIS.
    /// 
    /// 2. Get url's that have been rewritten.
    /// </summary>
    public class UrlHelper
    {
        private static bool _isRewritingEnabled = false;
        private static UrlMapper _urlMapper;

        public static Uri AbsoluteWebRoot
        {
            get
            {
                var context = HttpContext.Current;
                UriBuilder uri = new UriBuilder();
                uri.Host = context.Request.Url.Host;
                if (!context.Request.Url.IsDefaultPort)
                {
                    uri.Port = context.Request.Url.Port;
                }

                uri.Path = VirtualPathUtility.ToAbsolute("~/");

                Uri absoluteWebRoot = uri.Uri;

                return absoluteWebRoot;
            }
        }

        #region Properties
        /// <summary>
        /// Get or set flag indicating if url-rewriting is enabled.
        /// </summary>        
        public static bool IsRewritingEnabled
        {
            get { return _isRewritingEnabled; }
            set
            {
                _isRewritingEnabled = value;
                if (_isRewritingEnabled && _urlMapper == null)
                {
                    throw new InvalidOperationException("Must provide url mapping if performing url-rewriting.");
                }
            }
        }


        /// <summary>
        /// Instance of the url mapper.
        /// </summary>
        public UrlMapper UrlMapperProvider
        {
            get { return _urlMapper; }
        }


        /// <summary>
        /// 站点目录路径
        /// </summary>
        public static string AppPath
        {
            get
            {
                string path = HttpContext.Current.Request.ApplicationPath;
                return ("/" == path) ? "" : path;
            }
        }
        #endregion

        /// <summary>
        /// 获得上传绝对目录
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string GetUploadFullPath(out string path, string uploadName = "")
        {
            string dir = "/UpLoad/" + (string.IsNullOrEmpty(uploadName) ? "" : uploadName + "/") + DateTime.Now.ToString("yyyy/MM/dd/");
            string fullDir = System.Web.HttpContext.Current.Server.MapPath(dir);

            path = dir;

            if (!Directory.Exists(fullDir))
            {
                Directory.CreateDirectory(fullDir);
            }

            return fullDir;
        }

        /// <summary>
        /// 返回目录相对应的物理目录
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public static string GetMapPathPath(string path)
        {
            if (string.IsNullOrEmpty(path))
            {
                return string.Empty;
            }

            path = path.TrimStart('~');
            path = path.TrimStart('/');
            path = string.Format("~/{0}", path);

            string pathName = Path.GetDirectoryName(HttpContext.Current.Server.MapPath(path));
            if (!Directory.Exists(pathName))
            {
                Directory.CreateDirectory(pathName);
            }

            return HttpContext.Current.Server.MapPath(path);
        }

        /// <summary>
        /// Gets the relative site url.
        /// </summary>
        /// <remarks>
        /// Use this method for getting relative site urls for resource files such as
        /// 1. Javascript
        /// 2. Images
        /// 3. Xml files
        /// </remarks>
        /// <param name="url">"~/Scripts/Javascript/UI.js"</param>
        /// <returns>
        /// For IIS PRODUCTION: /Scripts/Javascript/UI.js
        /// For VS  LOCAL DEV : /MyApp.WebSite/Scripts/Javascript/UI.js
        /// </returns>
        public static string GetRelativeSiteUrl(string url)
        {
            return GetRelativeSiteUrl(HttpContext.Current.Request.ApplicationPath, url);
        }


        /// <summary>
        /// Gets the relative site url for possibly mapped/rewritten urls.
        /// </summary>
        /// <remarks>
        /// Use this method for getting relative site urls for rewritten urls such as
        /// 1. Web pages ( .aspx .html .htm etc. )
        /// </remarks>
        /// <param name="url">"~/Modules/Home.aspx"</param>
        /// <returns>
        /// If there are mappings:
        /// For IIS PRODUCTION: /Home.aspx
        /// For VS  LOCAL DEV : /Workshops.WebSite/Home.aspx
        /// </returns>        
        public static string GetMappedRelativeSiteUrl(string url)
        {
            return GetMappedRelativeSiteUrl(HttpContext.Current.Request.ApplicationPath, url); ;
        }


        /// <summary>
        /// Configure the url-rewriting flag and set the url mapper.
        /// </summary>
        /// <param name="isUrlRewritingEnabled">True if URL rewriting is enabled.</param>
        /// <param name="urlMapper">Instance of URL mapper.</param>
        public static void ConfigureUrlRewriting(bool isUrlRewritingEnabled, UrlMapper urlMapper)
        {
            _isRewritingEnabled = isUrlRewritingEnabled;
            if (_isRewritingEnabled && urlMapper == null)
            {
                throw new InvalidOperationException("Must provide urlmapper if performing url-rewriting.");
            }

            _urlMapper = urlMapper;
        }


        /// <summary>
        /// Gets the root of the website. http: or https: plus the appropriate port.
        /// </summary>
        /// <returns>Root of the website.</returns>
        public static string GetSiteRoot()
        {
            string protocol = HttpContext.Current.Request.ServerVariables["SERVER_PORT_SECURE"];
            if (protocol == null || protocol == "0")
                protocol = "http://";
            else
                protocol = "https://";

            string port = HttpContext.Current.Request.ServerVariables["SERVER_PORT"];
            if (port == null || port == "80" || port == "443")
                port = "";
            else
                port = ":" + port;

            string siteRoot = protocol + HttpContext.Current.Request.ServerVariables["SERVER_NAME"] + port + HttpContext.Current.Request.ApplicationPath;
            return siteRoot;
        }


        /// <summary>
        /// Returns the name of the requested file.
        /// </summary>
        /// <param name="rawUrl">Raw URL.</param>
        /// <param name="includeExtension">Flag indicating if extension of the file should also be 
        /// included.</param>
        /// <returns>Requested file name.</returns>
        public static string GetRequestedFileName(string rawUrl, bool includeExtension)
        {
            string file = rawUrl.Substring(rawUrl.LastIndexOf("/") + 1);

            if (includeExtension)
                return file;

            return file.Substring(0, file.IndexOf("."));
        }


        /// <summary>
        /// These are exposed to the unit tests in NHelper.Tests.
        /// </summary>
        /// <param name="applicationPath">Application path.</param>
        /// <param name="url">Request URL.</param>
        /// <returns>Relative site URL.</returns>
        internal static string GetRelativeSiteUrl(string applicationPath, string url)
        {
            // Get proper application path ending with "/"
            if (!applicationPath.EndsWith("/"))
            {
                applicationPath += "/";
            }

            // Remove the "~/" from the url since we are using application path.
            if (!string.IsNullOrEmpty(url) && url.StartsWith("~/"))
            {
                url = url.Substring(2, url.Length - 2);
            }
            return applicationPath + url;
        }


        /// <summary>
        /// These are exposed to the unit tests in ServiceGoFor.NHelper.Tests.
        /// </summary>
        /// <param name="applicationPath">Application path.</param>
        /// <param name="url">Request URL.</param>
        /// <returns>Mapped relative site URL.</returns>
        internal static string GetMappedRelativeSiteUrl(string applicationPath, string url)
        {
            // Check if rewriting enabled.
            if (!IsRewritingEnabled || _urlMapper == null) { return GetRelativeSiteUrl(applicationPath, url); }

            // Get the mapped url and return the valid relative site url.
            url = _urlMapper.GetUrl(url);
            return GetRelativeSiteUrl(applicationPath, url);
        }

        #region SEO Url
        /// <summary>
        /// Same as BuildValidUrl but uses RegEx and is much, much slower.
        /// </summary>
        /// <param name="title">URL.</param>
        /// <returns>Valid URL.</returns>
        public static string BuildValidUrlUsingRegex(string title)
        {
            //Replace . with – dash
            string newTitle = Regex.Replace(title.Trim(), @"\W", "-");

            //Replace multiple “-” dashes with single “-” dash
            newTitle = Regex.Replace(newTitle, @"55+", "-").Trim('-');

            return newTitle.ToLower();
        }


        /// <summary>
        /// Generates an SEO optimized url.
        /// </summary>
        /// <param name="title">URL.</param>
        /// <returns>Valid URL.</returns>
        public static string BuildValidUrl(string title)
        {
            // Validate.
            if (string.IsNullOrEmpty(title)) return string.Empty;

            // Get all lowercase without spaces.
            title = title.ToLower().Trim();
            string InvalidSeoUrlChars = @"$%#@!*?;:~`_+=()[]{}|\'<>,/^&"".";

            StringBuilder buffer = new StringBuilder();
            char currentChar, lastAddedChar = 'a';

            // Now go through each character.
            for (int ndx = 0; ndx < title.Length; ndx++)
            {
                currentChar = title[ndx];

                // Invalid char ? Go to next one.
                if (InvalidSeoUrlChars.Contains(currentChar.ToString()))
                {
                    continue;
                }

                // Handle spaces or dashes.
                if (currentChar == ' ' || currentChar == '-')
                {
                    // Only add if the previous char was not a space or dash (' ', '-').
                    // This is to avoid having multiple "-" dashes in the url.
                    if (lastAddedChar != ' ' && lastAddedChar != '-')
                    {
                        buffer.Append('-');
                        lastAddedChar = '-';
                    }
                }
                else
                {
                    buffer.Append(currentChar);
                    lastAddedChar = currentChar;
                }
            }
            return buffer.ToString();
        }
        #endregion

        static System.Text.Encoding encoding = System.Text.Encoding.UTF8;

        #region URL的64位编码
        public static string Base64Encrypt(string sourthUrl)
        {
            string eurl = HttpUtility.UrlEncode(sourthUrl);
            eurl = Convert.ToBase64String(encoding.GetBytes(eurl + "ABC"));
            return eurl;
        }
        #endregion

        #region URL的64位解码
        public static string Base64Decrypt(string eStr)
        {
            if (!IsBase64(eStr))
            {
                return eStr;
            }
            byte[] buffer = Convert.FromBase64String(eStr);
            string sourthUrl = encoding.GetString(buffer);
            sourthUrl = HttpUtility.UrlDecode(sourthUrl);
            return sourthUrl.Remove(sourthUrl.Length - 3);
        }
        /// <summary>
        /// 是否是Base64字符串
        /// </summary>
        /// <param name="eStr"></param>
        /// <returns></returns>
        public static bool IsBase64(string eStr)
        {
            if ((eStr.Length % 4) != 0)
            {
                return false;
            }
            if (!Regex.IsMatch(eStr, "^[A-Z0-9/+=]*$", RegexOptions.IgnoreCase))
            {
                return false;
            }
            return true;
        }
        #endregion

        /// <summary>
        /// 添加URL参数
        /// </summary>
        public static string AddParam(string url, string paramName, string value)
        {
            Uri uri = new Uri(url);
            if (string.IsNullOrEmpty(uri.Query))
            {
                string eval = HttpContext.Current.Server.UrlEncode(value);
                return String.Concat(url, "?" + paramName + "=" + eval);
            }
            else
            {
                string eval = HttpContext.Current.Server.UrlEncode(value);
                return String.Concat(url, "&" + paramName + "=" + eval);
            }
        }

        /// <summary>
        /// 更新URL参数
        /// </summary>
        public static string UpdateParam(string url, string paramName, string value)
        {
            string keyWord = paramName + "=";
            int index = url.IndexOf(keyWord) + keyWord.Length;
            int index1 = url.IndexOf("&", index);
            if (index1 == -1)
            {
                url = url.Remove(index, url.Length - index);
                url = string.Concat(url, value);
                return url;
            }
            url = url.Remove(index, index1 - index);
            url = url.Insert(index, value);
            return url;
        }

        #region 分析URL所属的域

        /// <summary>
        /// 分析 url 字符串中的参数信息
        /// </summary>
        /// <param name="url">输入的 URL</param>
        /// <param name="baseUrl">输出 URL 的基础部分</param>
        /// <param name="nvc">输出分析后得到的 (参数名,参数值) 的集合</param>
        public static void ParseUrl(string url, out string baseUrl, out NameValueCollection nvc)
        {
            if (url == null)
                throw new ArgumentNullException("url");

            nvc = new NameValueCollection();
            baseUrl = "";

            if (url == "")
                return;

            int questionMarkIndex = url.IndexOf('?');

            if (questionMarkIndex == -1)
            {
                baseUrl = url;
                return;
            }
            baseUrl = url.Substring(0, questionMarkIndex);
            if (questionMarkIndex == url.Length - 1)
                return;
            string ps = url.Substring(questionMarkIndex + 1);

            // 开始分析参数对    
            Regex re = new Regex(@"(^|&)?(\w+)=([^&]+)(&|$)?", RegexOptions.Compiled);
            MatchCollection mc = re.Matches(ps);

            foreach (Match m in mc)
            {
                nvc.Add(m.Result("$2").ToLower(), m.Result("$3"));
            }
        }

        #endregion

        /// <summary>
        /// 获得UrlQuery参数值
        /// </summary>
        /// <param name="Parameters"></param>
        /// <returns></returns>
        public static string GetQueryStringValue(string Parameters)
        {
            NameValueCollection gb2312Requests;
            gb2312Requests = HttpUtility.ParseQueryString(HttpContext.Current.Request.Url.Query, Encoding.GetEncoding("GB2312"));
            return gb2312Requests[Parameters];  //'里面的string就是你提交的参数的Key
        }
    }



    /// <summary>
    /// Url mapper to assist in mapping physical urls to possible rewritten urls.
    /// </summary>
    public class UrlMapper
    {
        private IDictionary<string, string> _urlMappings;


        /// <summary>
        /// Url mapper with mappings provided as IDictionary.
        /// </summary>
        /// <example>
        ///           Physical Url        Rewritten url
        ///     key = "~/Classes.aspx"    value = "~/SearchClasses.aspx"
        /// </example>
        /// <param name="urlMappings">Mappings from physical url to logical urls.</param>
        public UrlMapper(IDictionary<string, string> urlMappings)
        {
            Init(urlMappings);
        }


        /// <summary>
        /// Get the real url.
        /// </summary>
        /// <param name="url">URL.</param>
        /// <returns>Mapped url if mapping present, original url otherwise</returns>
        public string GetUrl(string url)
        {
            string urlLowerCase = url.ToLower();

            // Not mapped ? Return original
            if (_urlMappings.ContainsKey(urlLowerCase))
            {
                return _urlMappings[urlLowerCase];
            }

            // No mapping, return original
            return url;
        }


        /// <summary>
        /// Initialize.
        /// </summary>
        /// <param name="urlMappings">mappings to rewritten urls.</param>
        private void Init(IDictionary<string, string> urlMappings)
        {
            _urlMappings = new Dictionary<string, string>();

            // Store the mappings with the physical url in lowercase.
            IEnumerator<KeyValuePair<string, string>> enumerator = urlMappings.GetEnumerator();

            while (enumerator.MoveNext())
            {
                string key = enumerator.Current.Key.ToLower();
                _urlMappings.Add(key, enumerator.Current.Value);
            }
        }
    }
}
